/**
 * 报表设计公司(2011)
 */
package net.abbp.swing.controls.combobox;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionAdapter;
import java.awt.event.MouseMotionListener;

import javax.swing.BorderFactory;
import javax.swing.DefaultListCellRenderer;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.MenuSelectionManager;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.plaf.ComboBoxUI;
import javax.swing.plaf.basic.ComboPopup;
import javax.swing.plaf.metal.MetalComboBoxUI;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreeCellRenderer;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import com.sun.java.swing.plaf.motif.MotifComboBoxUI;
import com.sun.java.swing.plaf.windows.WindowsComboBoxUI;

/**
 * 树形下拉框
 * 
 * @version 1.0 2011-3-28
 * @author <a herf="lexloo@gmail.com">lexloo</a>
 * @since ANNCSR 2.0
 */
@SuppressWarnings("restriction")
public class JTreeComboBox extends JComboBox {
    /**
     * UID
     */
    private static final long serialVersionUID = -4345557817056855245L;
    /**
     * 显示用的树
     */
    private JTree tree;

    public JTreeComboBox() {
        this(new JTree());
    }

    public JTreeComboBox(JTree tree) {
        this.setTree(tree);
    }

    /**
     * 设置树
     * 
     * @param tree JTree
     */
    public void setTree(JTree tree) {
        this.tree = tree;
        if (tree != null) {
            this.setSelectedItem(tree.getSelectionPath());
            this.setRenderer(new JTreeComboBoxRenderer());
        }
        this.updateUI();
    }

    /**
     * 取得树
     * 
     * @return JTree
     */
    public JTree getTree() {
        return tree;
    }

    /**
     * 设置当前选择的树路径
     * 
     * @param o Object
     */
    public void setSelectedItem(Object o) {
        tree.setSelectionPath((TreePath) o);
        getModel().setSelectedItem(o);
    }

    /**
     * 获取当前选择的节点
     * 
     * @return 当前选择的节点
     */
    public DefaultMutableTreeNode getSelectedTreeNode() {
        TreePath tp = (TreePath) this.getSelectedItem();

        return tp == null ? null : (DefaultMutableTreeNode) tp.getLastPathComponent();
    }

    public void updateUI() {
        ComboBoxUI cui = (ComboBoxUI) UIManager.getUI(this);
        if (cui instanceof MetalComboBoxUI) {
            cui = new MetalJTreeComboBoxUI();
        } else if (cui instanceof MotifComboBoxUI) {
            cui = new MotifJTreeComboBoxUI();
        } else {
            cui = new WindowsJTreeComboBoxUI();
        }
        setUI(cui);
    }

    // UI Inner classes -- one for each supported Look and Feel
    class MetalJTreeComboBoxUI extends MetalComboBoxUI {
        protected ComboPopup createPopup() {
            return new TreePopup(comboBox);
        }
    }

    private class WindowsJTreeComboBoxUI extends WindowsComboBoxUI {
        protected ComboPopup createPopup() {
            return new TreePopup(comboBox);
        }
    }

    @SuppressWarnings("serial")
    private class MotifJTreeComboBoxUI extends MotifComboBoxUI {
        protected ComboPopup createPopup() {
            return new TreePopup(comboBox);
        }
    }

    /**
     * 
     * Description: 树形结构而来的DefaultListCellRenderer
     */
    @SuppressWarnings("serial")
    class JTreeComboBoxRenderer extends DefaultListCellRenderer {
        public Component getListCellRendererComponent(JList list,
                                                      Object value,
                                                      int index,
                                                      boolean isSelected,
                                                      boolean cellHasFocus) {
            if (value != null) {
                TreePath path = (TreePath) value;
                TreeNode node = (TreeNode) path.getLastPathComponent();
                value = node;
                TreeCellRenderer r = tree.getCellRenderer();
                JLabel lb =
                        (JLabel) r.getTreeCellRendererComponent(tree,
                                                                value,
                                                                isSelected,
                                                                false,
                                                                node.isLeaf(),
                                                                index,
                                                                cellHasFocus);
                return lb;
            }
            return super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
        }
    }

    /**
     * <p>
     * Description: TreePopup
     * </p>
     */
    private class TreePopup extends JPopupMenu implements ComboPopup {
        /**
         * UID
         */
        private static final long serialVersionUID = 1427725255215348419L;
        protected JTreeComboBox comboBox;
        protected JScrollPane scrollPane;

        protected MouseMotionListener mouseMotionListener;
        protected MouseListener mouseListener;
        private MouseListener treeSelectListener = new MouseAdapter() {
            public void mouseReleased(MouseEvent e) {
                JTree tree = (JTree) e.getSource();
                TreePath tp = tree.getPathForLocation(e.getPoint().x, e.getPoint().y);
                if (tp == null) {
                    return;
                }
                comboBox.setSelectedItem(tp);
                togglePopup();
                MenuSelectionManager.defaultManager().clearSelectedPath();
            }
        };

        public TreePopup(JComboBox comboBox) {
            this.comboBox = (JTreeComboBox) comboBox;
            setBorder(BorderFactory.createLineBorder(Color.BLACK));
            setLayout(new BorderLayout());
            setLightWeightPopupEnabled(comboBox.isLightWeightPopupEnabled());
            JTree tree = this.comboBox.getTree();
            if (tree != null) {
                scrollPane = new JScrollPane(tree);
                scrollPane.setBorder(null);
                add(scrollPane, BorderLayout.CENTER);
                tree.addMouseListener(treeSelectListener);
            }
        }

        public void show() {
            updatePopup();
            show(comboBox, 0, comboBox.getHeight());
            int x = 0;
            int y = comboBox.getHeight() - 1;

            Point invokerOrigin;
            invokerOrigin = comboBox.getLocationOnScreen();

            // To avoid integer overflow
            long lx, ly;
            lx = ((long) invokerOrigin.x) + ((long) x);
            ly = ((long) invokerOrigin.y) + ((long) y);
            if (lx > Integer.MAX_VALUE)
                lx = Integer.MAX_VALUE;
            if (lx < Integer.MIN_VALUE)
                lx = Integer.MIN_VALUE;
            if (ly > Integer.MAX_VALUE)
                ly = Integer.MAX_VALUE;
            if (ly < Integer.MIN_VALUE)
                ly = Integer.MIN_VALUE;

            setLocation((int) lx, (int) ly);

            setVisible(true);
            comboBox.getTree().requestFocus();
        }

        public void hide() {
            setVisible(false);
            comboBox.firePropertyChange("popupVisible", true, false);
        }

        protected JList list = new JList();

        public JList getList() {
            return list;
        }

        public MouseMotionListener getMouseMotionListener() {
            if (mouseMotionListener == null) {
                mouseMotionListener = new MouseMotionAdapter() {};
            }
            return mouseMotionListener;
        }

        public KeyListener getKeyListener() {
            return null;
        }

        public void uninstallingUI() {}

        /**
         * Implementation of ComboPopup.getMouseListener().
         * 
         * @return a <code>MouseListener</code> or null
         * @see ComboPopup#getMouseListener
         */
        public MouseListener getMouseListener() {
            if (mouseListener == null) {
                mouseListener = new InvocationMouseHandler();
            }
            return mouseListener;
        }

        protected void togglePopup() {
            if (isVisible()) {
                hide();
            } else {
                show();
            }
        }

        protected void updatePopup() {
            setPreferredSize(new Dimension(comboBox.getSize().width, 200));
            Object selectedObj = comboBox.getSelectedItem();
            if (selectedObj != null) {
                TreePath tp = (TreePath) selectedObj;
                ((JTreeComboBox) comboBox).getTree().setSelectionPath(tp);
            }
        }

        protected class InvocationMouseHandler extends MouseAdapter {
            public void mousePressed(MouseEvent e) {
                if (!SwingUtilities.isLeftMouseButton(e) || !comboBox.isEnabled()) {
                    return;
                }
                if (comboBox.isEditable()) {
                    Component comp = comboBox.getEditor().getEditorComponent();
                    if ((!(comp instanceof JComponent))
                        || ((JComponent) comp).isRequestFocusEnabled()) {
                        comp.requestFocus();
                    }
                } else if (comboBox.isRequestFocusEnabled()) {
                    comboBox.requestFocus();
                }
                togglePopup();
            }
        }
    }
}
